/**
 * 判断字符是否为空
 * @param {String} str 字符
 * @returns {Boolean}
 * 使用示例：isEmpty(' 222') false
 */
export function isEmpty(str) {
	try {
		return str.trim().length == 0
	} catch (e) {
		return true;
	}
}
/**
 * 获取字符串中的链接
 * @param {Object} str
 */
export function getHttpStr(str) {
	const reg = /(https?|http|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g;
	const s = str.match(reg);
	return (s && s.length ? s[0] : null);
}
/**
 * 递归方式深拷贝数据
 * @param {Object|Array} source 需要拷贝的对象
 * @returns {Object|Array}
 * 使用示例：deepClone([1,2,3,4])
 */
export function deepClone(source) {
	if (!source && typeof source !== 'object') {
		throw new Error('error arguments', 'deepClone')
	}
	const targetObj = source.constructor === Array ? [] : {}
	Object.keys(source).forEach(keys => {
		if (source[keys] && typeof source[keys] === 'object') {
			targetObj[keys] = deepClone(source[keys])
		} else {
			targetObj[keys] = source[keys]
		}
	})
	return targetObj
}
/**
 * 数组根据某个字段进行排序
 * @param {Array} arr 数组
 * @param {String} k 字符串
 * @param {Boolean} up 是否正序 默认true
 * @returns {Array}
 * 使用示例：sort([{a:1},{a:3},{2},{a:0}],'a',false)
 */
export function sort(arr, k, up = true) {
	if (!Array.isArray(arr)) {
		throw new Error('数据类型必须是数组');
	}
	return arr.sort(compare(k, up));
}
/**
 * 为了满足数组根据某个字段进行排序
 * @param {*} k 数组关键词 
 * @param {*} up 是否是正序 默认true
 */
export function compare(k, up = true) {
	return function(a, b) {
		var v1 = a[k];
		var v2 = b[k];
		return up ? v1 - v2 : v2 - v1;
	}
}
/**
 * 根据对象用&拼接返回
 * @param {Object} json 对象
 * @returns {String}
 * 使用示例：param({a:1,b:2}) 返回：a=1&b=2
 */
export function param(json) {
	if (!json) return ''
	let newjson = Object.keys(json).map(key => {
		if (json[key] === undefined) return ''
		return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
	})
	return newjson.join('&');
}
/**
 * 根据时间戳+随机数生成32进制的字符串
 * @returns {string}
 * 使用示例：createUniqueString()
 */
export function createUniqueString() {
	const timestamp = +new Date() + ''
	const randomNum = parseInt((1 + Math.random()) * 65536) + ''
	return (+(randomNum + timestamp)).toString(32)
}
/**
 * 根据地址栏返回参数对象
 * @param {string} url 地址 非必传
 * @returns {Object}
 * getQueryObject('http://localhost:8080/demo#/demo?id=1&ff=2233'); 返回：{id: '1', ff: '2233'}
 */
export function getQueryObject(url) {
	url = url == null ? window.location.href : url
	const search = url.substring(url.lastIndexOf('?') + 1)
	const obj = {}
	const reg = /([^?&=]+)=([^?&=]*)/g
	search.replace(reg, (rs, $1, $2) => {
		const name = decodeURIComponent($1)
		let val = decodeURIComponent($2)
		val = String(val)
		obj[name] = val
		return rs
	})
	return obj
}
/**
 * 防抖
 * @param {Function} fn 回调函数
 * @param {number} dealy 等待时间
 * */
export function debounce(fn, dealy) {
	let timer = null;
	return function() {
		if (timer) {
			clearTimeout(timer)
		}
		timer = setTimeout(fn, dealy)
	}
}
/**
 * 节流
 * @param {Function} fn 回调函数
 * @param {number} dealy 等待时间
 * */
export function throttle(fn, dealy) {
	let flag = true;
	return function() {
		if (!flag) {
			return false;
		}
		flag = false;
		setTimeout(() => {
			fn();
			flag = true;
		}, dealy)
	}
}
/**
 * 数组去重
 * @param {Object} array 数组
 * @returns {Array}
 * 使用示例：removeRepeat([1,2,3,4,1,1]);返回[1,2,3,4]
 */
export function removeRepeat(array) {
	return Array.from(new Set(array));
}
/**
 * 字符串去掉转移符号
 * @param {Object} str
 * @returns {String}
 * 使用示例：removeSymbol('gdhjk\b')
 */
export function removeSymbol(str) {
	return str.replace(/[\'\"\\\/\b\f\n\r\t]/g, '');
}
/**
 * Merges two objects, giving the last one precedence
 * @param {Object} target
 * @param {(Object|Array)} source
 * @returns {Object}
 * 使用示例：objectMerge({a:1},{b:2});返回:{a:1,b:2}
 */
export function objectMerge(target, source) {
	if (typeof target !== 'object') {
		target = {}
	}
	if (Array.isArray(source)) {
		return source.slice()
	}
	Object.keys(source).forEach(property => {
		const sourceProperty = source[property]
		if (typeof sourceProperty === 'object') {
			target[property] = objectMerge(target[property], sourceProperty)
		} else {
			target[property] = sourceProperty
		}
	})
	return target
}
// =============时间相关=================
/**
 * Parse the time to string 处理时间 自定义返回形式
 * @param {(Object|string|number)} time
 * @param {string} cFormat
 * @returns {string | null}
 * 使用示例：parseTime(new Date(),'{y}-{m}-{d} {h}:{i}:{s} 今天是星期{a}'); 返回：2021-10-15 17:52:38 今天是星期五
 */
export function parseTime(time, cFormat) {
	if (arguments.length === 0 || !time) {
		return null
	}
	const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
	let date
	if (typeof time === 'object') {
		date = time
	} else {
		if ((typeof time === 'string')) {
			if ((/^[0-9]+$/.test(time))) {
				// support "1548221490638"
				time = parseInt(time)
			} else {
				// support safari
				// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
				time = time.replace(new RegExp(/-/gm), '/')
			}
		}
		if ((typeof time === 'number') && (time.toString().length === 10)) {
			time = time * 1000
		}
		date = new Date(time)
	}
	const formatObj = {
		y: date.getFullYear(),
		m: date.getMonth() + 1,
		d: date.getDate(),
		h: date.getHours(),
		i: date.getMinutes(),
		s: date.getSeconds(),
		a: date.getDay()
	}
	const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
		const value = formatObj[key]
		// Note: getDay() returns 0 on Sunday
		if (key === 'a') {
			return ['日', '一', '二', '三', '四', '五', '六'][value]
		}
		return value.toString().padStart(2, '0')
	})
	return time_str
}
/**
 * 获取当天时间段
 * @returns {Array}
 * 使用示例：getCurrentDay();返回：["2020-05-20 00:00:00", "2020-5-20 23:59:59"]
 */
export function getCurrentDay() {
	var date = new Date();
	let year = date.getFullYear();
	let month = ((date.getMonth() + 1) < 10 ? "0" + (date.getMonth() + 1) : (date.getMonth() + 1));
	let day = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate());
	var startTime = year + "-" + month + "-" + day + " " + "00:00:00";
	var endTime = year + '-' + month + '-' + day + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds();
	return [startTime, endTime]
}
/**
 * 获取时间段相隔的小时数
 * @param {string} time1 第1个时间
 * @param {string} time2 第2个时间
 * @returns {Number}
 */
export function timeRangeHour(time1, time2) {
	let usedTime = new Date(time2).getTime() - new Date(time1).getTime();
	return (Math.ceil((usedTime / 1000) / 60) / 60);
}
/**
 * 倒计时
 * @param {Object} time 秒
 * @param {Object} fn 回调函数
 * @returns {Number}
 * 使用示例：countdown(60,(s)=>{console.log(s)}); 返回秒
 */
export function countdown(time = 10, fn) {
	let _t = time,
		_timer = null;
	if (fn) fn(_t);
	_timer = setInterval(() => {
		_t--;
		if (fn) fn(_t);
		if (_t <= 0) {
			clearInterval(_timer);
		}
	}, 1000)
}
/**
 * 判断是否是函数
 * @param {Object} val 值
 * @returns {Boolean}
 */
export function isFunction(val) {
	return typeof val === 'function';
}
/**
 * 设置缓存 (单位为秒)
 * @param {Object|Number|String} value 值
 * @param {String} key 设置缓存的关键词
 * @param {String} dateStamp 时间戳
 */
export const setStorage = (value, key = ACCESS_TOKEN, dateStamp = null) => {
	const params = {
		date: dateStamp ? dateStamp == '0.0.0.0' ? new Date().setHours(0, 0, 0, 0) : dateStamp : new Date().getTime(),
		value
	};
	wx.setStorageSync(key, JSON.stringify(params));
}
/**
 * 获取缓存
 * @param {*} day 天数 1代表24小时
 * @param {*} key 获取缓存的关键词
 */
export const getStorage = (day = 1, key = ACCESS_TOKEN) => {
	let obj = wx.getStorageSync(key);
	if (!obj) return null;
	obj = JSON.parse(obj);
	const date = new Date().getTime();
	if (date - obj.date > 24 * 60 * 60 * 1000 * day) return null;
	return obj.value;
}
/**
 * 清除缓存
 * @param {*} key 清除缓存的关键词
 */
export const removeStorage = (key = ACCESS_TOKEN) => {
	wx.removeStorageSync(key);
}
/**
 * 图片转换成base64
 * @param {*} url 图片地址
 * @param {*} prefix 是否拼接前缀 默认true
 */
export function imgUrlToBase64(url, prefix = true) {
	return new Promise((reslove) => {
		if (!url) return reslove({
			code: -1,
			msg: '需要传入图片地址'
		});
		// #ifdef MP-WEIXIN
		uni.getFileSystemManager().readFile({
			filePath: url,
			encoding: 'base64',
			success: res => {
				let base64 = prefix ? 'data:image/jpeg;base64,' + res.data : res.data;
				reslove({
					code: 0,
					msg: '转换成功',
					data: base64
				});
			},
			fail: (e) => {
				reslove({
					code: -1,
					msg: `图片转换失败：${JSON.stringify(e)}`
				});
			}
		})
		// #endif
		// #ifdef H5
		uni.request({
			url: url,
			method: 'GET',
			responseType: 'arraybuffer',
			success: ress => {
				let base64 = uni.arrayBufferToBase64(ress.data); //把arraybuffer转成base64
				base64 = prefix ? 'data:image/jpeg;base64,' + base64 : base64;
				reslove({
					code: 0,
					msg: '转换成功',
					data: base64
				});
			},
			fail: (e) => {
				reslove({
					code: -1,
					msg: `图片转换失败：${JSON.stringify(e)}`
				});
			}
		})
		// #endif
		// #ifdef APP-PLUS
		plus.io.resolveLocalFileSystemURL(url, (entry) => {
			// 可通过entry对象操作test.html文件 
			entry.file((file) => {
				let fileReader = new plus.io.FileReader();
				fileReader.onloadend = (evt) => {
					const base64 = evt.target.result.substr(22);
					reslove({
						code: 0,
						msg: '转换成功',
						data: base64
					});
				}
				fileReader.readAsDataURL(file);
			});
		}, (e) => {
			reslove({
				code: -1,
				msg: `图片转换失败：${JSON.stringify(e)}`
			});
		});
		// #endif
	})
}
/**
 * 复制文本
 * @param {string} v 复制的文本
 * @param {string} msg 提示文本
 * @param {string} icon 提示类型
 * @param {string} type 提示类型
 */
export function copyContent(v, msg = '', icon = 'success', type = 0) {
	return new Promise((resolve, reject) => {
		uni.setClipboardData({
			data: v,
			success: res => {
				setTimeout(() => {
					resolve(v);
				}, 10)
			},
			fail: error => {
				reject('复制失败');
			},
			complete: res => {
				uni.hideToast();
			}
		})
	})
}
// 微信浏览器
export function wxUA() {
	return /MicroMessenger/.test(window.navigator.userAgent)
}
// 支付宝浏览器
export function aliUA() {
	return /AlipayClient/.test(window.navigator.userAgent)
}
// 模拟a标签点击
export function imitateClick(url, targetName) {
	var aEle = document.createElement("a");
	aEle.setAttribute("href", url);
	aEle.setAttribute("target", targetName || "_blank");
	aEle.setAttribute("id", "previewJumpEle");
	// 防止重复添加
	if (!document.getElementById("previewJumpEle")) {
		document.body.appendChild(aEle);
	}
	// 模拟点击
	aEle.click();
	(aEle.remove && aEle.remove()) || (aEle.removeNode && aEle.removeNode(true));
}
/**
 * 标点符号等转换成英文模式
 * @param {string} text 字符
 */
export function chinesePunctuationToEnglish(text) {
	// 创建一个替换映射表
	const replacementMap = {
		'，': ',',
		'。': '.',
		'！': '!',
		'？': '?',
		'；': ';',
		'：': ':',
		'“': '"',
		'”': '"',
		'‘': "'",
		'’': "'",
		'（': '(',
		'）': ')',
		'《': '<',
		'》': '>',
		'、': ',',
		'·': '.',
		'—': '-',
		'……': '...',
		'＂': '"',
		'＇': "'",
		'［': '[',
		'］': ']',
		'〔': '{',
		'〕': '}',
		'【': '[',
		'】': ']',
		'－': '-',
	};
	// 构建正则表达式，匹配所有中文标点符号
	const regex = new RegExp(`[${Object.keys(replacementMap).join('')}]`, 'g');
	// 使用正则表达式的replace方法进行替换
	return text.replace(regex, (match) => replacementMap[match]);
}
/**
 * 将数组根据一定数量进行分组
 * @param {Object} arr 数据源
 * @param {Object} count 分组的数量
 */
export function arrGorup(arr, count) {
	let groupedList = [];
	for (let i = 0; i < arr.length; i += count) {
		groupedList.push(arr.slice(i, i + count));
	}
	return groupedList;
}
/**
 * 获取随机整数
 * @param {Object} min 最小值
 * @param {Object} max 最大值
 * @param {Array} exclude 排除的整数
 */
export function getRandomInt(min = 0, max = 10, exclude = []) {
	min = Math.ceil(min);
	max = Math.floor(max);
	let randomInt;
	do {
		randomInt = Math.floor(Math.random() * (max - min + 1)) + min;
	} while (exclude.includes(randomInt));
	return randomInt;
}
/**
 * 随机重新排序或打乱数组的顺序
 * @param {Object} array 数据源
 */
export function shuffleArray(array) {
	for (let i = array.length - 1; i > 0; i--) {
		const j = Math.floor(Math.random() * (i + 1));
		[array[i], array[j]] = [array[j], array[i]];
	}
	return array;
}
/**
 * 循环来重复合并数组
 * @param {Object} array 数据源
 * @param {Number} count 次数
 */
export function repeatArray(array, count) {
	let newArray = [];
	for (let i = 0; i < count; i++) {
		newArray = [...newArray, ...array];
	}
	return newArray;
}
/**
 * 根据天数获取过去的天数，inToday-是否包括今天
 * @param {Number} day 天数  0-今天的0点 inToday必为true
 * @param {Boolean} inToday 是否包括今天
 */
export function getLastDaysTime(day = 0, inToday = false) {
	if (day == 0) inToday = true;
	let startTime = null;
	let endTime = null;
	const today = new Date();
	today.setHours(0, 0, 0, 0); // 设置为今天的开始时间
	const lastDays = new Date(today);
	lastDays.setDate(today.getDate() - day);
	startTime = lastDays.getTime();
	if (inToday) {
		const toDays = new Date(today);
		toDays.setDate(today.getDate() + 1);
		toDays.setTime(toDays.getTime() - 1);
		endTime = toDays.getTime();
	} else {
		today.setTime(today.getTime() - 1);
		endTime = today.getTime();
	}
	return {
		startTime,
		endTime
	}
}
/**
 * 给浏览器添加参数，并无刷新状态
 * @param {Object} name 参数名
 * @param {Object} value 参数值
 */
export function addUrlParameter(name, value) {
	// 获取当前URL
	var url = new URL(window.location.href);
	// 设置新的查询参数
	url.searchParams.set(name, value);
	// 使用history.pushState()改变地址栏的URL
	history.pushState(null, '', url);
}
/**
 * 给浏览器删除参数，并无刷新状态
 * @param {string} paramKey 参数名
 */
export function removeUrlParameter(paramKey) {
	const url = window.location.href;
	const r = url.split("?");
	if (r.length > 1) {
		const para = r[1].split("&");
		let newUrl = r[0] + "?";
		for (let i = 0; i < para.length; i++) {
			const p = para[i].split("=");
			if (p[0] != paramKey) {
				newUrl += para[i] + "&";
			}
		}
		newUrl = newUrl.substring(0, newUrl.length - 1);
		window.history.pushState(null, null, newUrl);
	}
}
/**
 * 模拟元素过渡效果
 * @param {Object} element 元素
 */
export function animateHeight(element, height = 'auto') {
	switch (height) {
		case 'auto':
			// 获取元素的初始高度
			const realHeight = element.scrollHeight;
			// 设置元素的样式为auto，以获取其真实高度
			element.style.height = 'auto';
			// 获取元素的真实高度
			const autoHeight = element.clientHeight;
			// 重置元素高度为0
			element.style.height = '0px';
			// 强制重排，确保之前的样式设置生效
			element.offsetHeight;
			// 动态设置元素的高度，并触发过渡动画
			element.style.height = autoHeight + 'px';
			element.addEventListener('transitionend', function onTransitionEnd() {
				// 动画结束后，设置height为auto
				element.style.height = 'auto';
				// 移除事件监听器
				element.removeEventListener('transitionend', onTransitionEnd);
			});
			break;
		default:
			const _autoHeight = element.clientHeight;
			element.style.height = _autoHeight + 'px';
			element.offsetHeight;
			element.style.height = height;
			break;
	}
}